Skip to content

Allow expressions in facet specs even when some variables are not available on some layers - #3757

Merged
yutannihilation merged 11 commits into
tidyverse:v3.3.0-rcfrom
yutannihilation:fix/issue-2963-tolerant-facet-rc
Jan 25, 2020
Merged

yutannihilation merged 11 commits into
tidyverse:v3.3.0-rcfrom
yutannihilation:fix/issue-2963-tolerant-facet-rc

Conversation

@yutannihilation

@yutannihilation yutannihilation commented Jan 22, 2020 •

Copy link
Copy Markdown
Member

(Cherry-picked version of #3735 for v3.3.0-rc branch)


Fix #2963

Background

Not all variables in a facet spec are available on all the layers. A solution suggested on r-lib/rlang#888 (comment) is:

bind the same symbols inside each panel, and when the symbols are undefined use an active binding to throw a typed error? And then you'd only catch these particular errors in the tryCatch()

Main change

This PR injects such an active bindings on the column names of all the plot data so that we can let eval_facet() fail gracefully.

eval_facet() evaluates facet specs in these two steps:

  1. When the spec is a symbol (the most common case), check if the layer data contains the same name of column. If it does, subset the column and return it. If it doesn't, return NULL. In this case, no error happens even if the symbol is not available on all the layers.
  2. When the spec is a expression, evaluate it. In this case, an error is raised on the layer where the symbol is not available.

This PR changes only step 2, so I expect this doesn't affect most of the existing code. Note that, while step 2 can also handle symbols, we still need step 1 to issue a friendly error ("At least one layer must contain all faceting variables") when no layer contain the variable.

devtools::load_all("~/repo/ggplot2/")
#> Loading ggplot2

# works fine with expressions
ggplot(mtcars, aes(mpg, cyl)) +
  geom_point() +
  geom_vline(xintercept = 20) +
  facet_wrap(vars(2 * am))

# works fine with external variables
two <- 2
ggplot(mtcars, aes(mpg, cyl)) +
  geom_point() +
  geom_vline(xintercept = 20) +
  facet_wrap(vars(two * am))

# raises an error when the expression refers to some non-existent variable
ggplot(mtcars, aes(mpg, cyl)) +
  geom_point() +
  geom_vline(xintercept = 20) +
  facet_wrap(vars(no_such_variable * am))
#> Error in eval_tidy(facet, mask): object 'no_such_variable' not found

# special case: raises a friendlier error when the expression is a symbol
ggplot(mtcars, aes(mpg, cyl)) +
  geom_point() +
  geom_vline(xintercept = 20) +
  facet_wrap(vars(no_such_variable))
#> Error: At least one layer must contain all faceting variables: `no_such_variable`.
#> * Plot is missing `no_such_variable`
#> * Layer 1 is missing `no_such_variable`
#> * Layer 2 is missing `no_such_variable`

# a data pronoun can be handled
ggplot(mtcars, aes(mpg, cyl)) +
  geom_point() +
  geom_vline(xintercept = 20) +
  facet_wrap(vars(.data$am))

Created on 2020-01-23 by the reprex package (v0.3.0)

Minor change

  • This PR removes env argument of eval_facet(). This is not necessary, but done in order to avoid confusion. I believe all facet specs are converted to quosures and bare expressions are not allowed, which means env is always ignored on eval_tidy().

@yutannihilation

Copy link
Copy Markdown
Member Author

May I merge this? I'll keep this open for a day or so in case we need further discussion. Feel free to merge if you need to run revdepchecks early.

@yutannihilation yutannihilation added this to the ggplot2 3.3.0 milestone Jan 23, 2020
@thomasp85

Copy link
Copy Markdown
Member

You can merge at your leisure - we won't run revdeps before after the conf anyway

@yutannihilation

Copy link
Copy Markdown
Member Author

Sure.

@yutannihilation
yutannihilation merged commit 5e6b1e5 into tidyverse:v3.3.0-rc Jan 25, 2020
@yutannihilation
yutannihilation deleted the fix/issue-2963-tolerant-facet-rc branch January 25, 2020 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants